package cn.edu.njnu.opengmpback.resource.service.Impl;

import cn.edu.njnu.opengmpback.Modeling.dao.ComputeModelDao;
import cn.edu.njnu.opengmpback.Modeling.dao.ProjectDao;
import cn.edu.njnu.opengmpback.Modeling.entity.ComputeModel;
import cn.edu.njnu.opengmpback.Modeling.entity.Project;
import cn.edu.njnu.opengmpback.Modeling.service.ComputeModelService;
import cn.edu.njnu.opengmpback.common.dto.PageDTO;
import cn.edu.njnu.opengmpback.common.utils.*;
import cn.edu.njnu.opengmpback.resource.dao.*;
import cn.edu.njnu.opengmpback.resource.entity.*;
import cn.edu.njnu.opengmpback.resource.entity.dto.*;
import cn.edu.njnu.opengmpback.resource.service.UserResService;
import cn.edu.njnu.opengmpback.resource.dao.ModelItemDao;
import cn.edu.njnu.opengmpback.resource.entity.ModelItem;
import cn.edu.njnu.opengmpback.user.dao.HistoryDao;
import cn.edu.njnu.opengmpback.user.dao.UserDao;
import cn.edu.njnu.opengmpback.user.entity.History;
import cn.edu.njnu.opengmpback.user.entity.UserEntity;
import cn.edu.njnu.opengmpback.user.entity.UserVo;
import cn.edu.njnu.opengmpback.user.service.HistoryService;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.text.MessageFormat;
import java.util.*;


@Slf4j
@Service
public class UserResServiceImpl implements UserResService {

    @Autowired
    UserDataDao userDataDao;

    @Autowired
    FolderDao folderDao;

    @Autowired
    ResourceDataDao resourceDataDao;

    @Autowired
    ThemeDao themeDao;

    @Autowired
    ModelItemDao modelItemDao;

    @Autowired
    ProjectDao projectDao;

    @Autowired
    ComputeModelDao computeModelDao;


    @Value("${dataStoreDir}"+"/data")
    private  String dataFolder;

    @Value("${dataStoreDir}")
    private  String dataStoreDir;

    @Autowired
    private UserDao userDao;

    @Autowired
    HistoryService historyService;

    @Value("${tempUploadDir}")
    String tempUploadDir;

    @Value("${dataStoreDir}"+"/resourceData")
    private  String resourceDataFolder;

    @Override
    public JsonResult createFolder(CreateFolderDTO createFolderDTO, String userId) {
        try{
            Folder folder=new Folder();
            BeanUtils.copyProperties(createFolderDTO,folder);

            folder.setUserId(userId);
            folderDao.save(folder);
            return ResultUtils.success(folder);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"创建文件夹失败");
        }
    }

    @Override
    public JsonResult saveDataItem(MultipartFile [] upFiles, DataItemDTO dataItemDTO, String userId) {
        //记录上传状态
        List<String> failed = new ArrayList<>();
        List<UserData> uploaded = new ArrayList<>();

        if (upFiles.length <= 0) {
            return ResultUtils.error(-1,"file is empty!!!");
        }
        UserEntity userEntity = userDao.findByUserId(userId);
        for (MultipartFile upFile : upFiles) {
            try {
                //首先将用户上传的数据保存到指定位置，不管是否可视化，都要保存在文件夹中
                UserData userData = new UserData();
                BeanUtils.copyProperties(dataItemDTO, userData);
                File folder = new File(dataFolder);
                if (!folder.isDirectory()) {
                    folder.mkdirs();
                }
                //将文件保存到指定位置
                String fileName = upFile.getOriginalFilename();
                String fileMainName = FileNameUtil.mainName(fileName);
                String fileExtName = FileNameUtil.extName(fileName);
                String fileNewName = fileMainName + IdUtil.objectId() + "." + fileExtName;
                File saveFile = new File(folder, fileNewName);
                upFile.transferTo(saveFile);

                userData.setName(fileName);
                userData.setUserId(userId);
                userData.setFileRelativePath("/data/" + fileNewName);
                userData.setFileWebAddress("/store/data/" + fileNewName);
                userData.setFileStoreName(fileNewName);
                userData.setType("file");
                userData.setSize(String.format("%.3f", saveFile.length() / 1048576.0) + " MB");
                userData.setInitial(true);
                userDataDao.save(userData);

                ArrayList<String> datalist = userEntity.getDatalist();
                datalist.add(userData.getId());
                userEntity.setDatalist(datalist);

                userDao.save(userEntity);
                uploaded.add(userData);
            }catch (Exception e){
                log.error(e.getMessage());
                failed.add(upFile.getOriginalFilename());
            }
    }
        JSONObject result = new JSONObject();
        result.put("uploaded",uploaded);
        result.put("failed",failed);
        return ResultUtils.success(result);
    }

    @Override
    public JsonResult saveBigFileData(DataItemDTO dataItemDTO, UserVo userVo) {
        try{
            File folder = new File(dataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            UserData userData= new UserData();
            BeanUtils.copyProperties(dataItemDTO,userData);

            //大文件
            String srcFilePath = tempUploadDir + dataItemDTO.getFileStoreName();
            FileUtil.copy(srcFilePath,dataFolder,true);
            userData.setFileWebAddress("/store/data/"+dataItemDTO.getFileStoreName());
            userData.setFileRelativePath("/data/"+dataItemDTO.getFileStoreName());
            FileUtil.del(srcFilePath); //删除大文件临时存在的位置
            userData.setUserId(userVo.getUserId());
            userData.setType("file");
            userData.setSize(String.format("%.3f", dataItemDTO.getFileStoreName().length() / 1048576.0) + " MB");
            userData.setInitial(true);
            userDataDao.save(userData);

            return ResultUtils.success(userData);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"保存资源数据失败！");
        }
    }

    public JsonResult publishData(CreateResourceDataDTO createResourceDataDTO, List<Map<String, String>> files){
        try {
            File folder = new File(resourceDataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            ResourceData resourceData= new ResourceData();
            BeanUtils.copyProperties(createResourceDataDTO,resourceData);

            // 创建目录保存发布的文件
            String savePathName = UUID.randomUUID().toString();
            File saveFolder = new File(savePathName);
            if (!saveFolder.isDirectory()) {
                saveFolder.mkdirs();
            }
            ArrayList<SubDataItem> subDataItems = resourceData.getSubDataItems();
            // 拷贝文件
            for(Map<String, String> element:files){
                String fileName = element.get("fileName");
                String filePath = element.get("filePath");
                File file = new File(filePath);
                SubDataItem subDataItem = new SubDataItem();
                FileUtil.copy(dataStoreDir + "/" + filePath, resourceDataFolder + "/" + savePathName + "/" + file.getName(), true); // 拷贝数据
                subDataItem.setName(fileName);
                subDataItem.setType(file.getName().substring(file.getName().lastIndexOf(".") + 1));
                subDataItem.setSize(String.format("%.3f", file.length() / 1048576.0) + " MB");
                subDataItem.setFileStoreName(resourceDataFolder + "/" + savePathName + "/" + file.getName());
//                subDataItem.setFileWebAddress();
                subDataItem.setProblemTags(resourceData.getProblemTags());
                subDataItem.setNormalTags(resourceData.getNormalTags());
                subDataItem.setUserId(resourceData.getUserEmail());
                subDataItem.setVisualWebAddress("");
                subDataItem.setPublicBoolean(true);
                subDataItem.setFileRelativePath( "/resourceData/" + savePathName + "/" + file.getName());
                subDataItems.add(subDataItem);
            }

            resourceData.setSubDataItems(subDataItems);
            resourceData.setUserEmail("opengms@126.com");
            resourceDataDao.save(resourceData);

            return ResultUtils.success(resourceData);
        }
        catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"发布数据失败");
        }
    }

    @Override
    public JsonResult updateUserDataItem(UpdateUserDataItemDTO updateUserDataItemDTO) {
        try {
            UserData userData = userDataDao.findById(updateUserDataItemDTO.getId()).get();
            BeanUtils.copyProperties(updateUserDataItemDTO, userData);
            userDataDao.save(userData);

            return ResultUtils.success(userData);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"更新数据项失败"+e.getMessage());
        }
    }

    @Override
    public JsonResult updateUserFolder(UpdateFolderDTO updateFolderDTO) {
        try {
            Folder folder=folderDao.findById(updateFolderDTO.getId()).get();
            BeanUtils.copyProperties(updateFolderDTO, folder);
            folderDao.save(folder);
            return ResultUtils.success(folder);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"更新文件夹信息失败"+e.getMessage());
        }
    }

    @Override
    public JsonResult getUserResource(String parentId, String userId) {
        try{

            JSONArray jsonArray=new JSONArray();

            //第一级目录的parentId是用户的id
//            List<Folder> folders = folderDao.findAllByUserIdAndParentId(userId, parentId);
            List<Folder> folders = folderDao.findAllByParentId(parentId);
            for(int i=0;i<folders.size();i++){
                jsonArray.add(folders.get(i));
            }

//            List<DataItem> dataItems=dataItemDao.findAllByUserIdAndParentId(userId,parentId);
            List<UserData> userData = userDataDao.findAllByParentId(parentId);
            for(int i = 0; i< userData.size(); i++){
                jsonArray.add(userData.get(i));
            }

            return ResultUtils.success(jsonArray);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"查询用户文件资源失败");
        }
    }

    @Override
    public JsonResult getUserAllResource(String userId) {
        try {
            JSONObject o=new JSONObject();

//            JSONArray personalDataArray=new JSONArray();
            List<UserData> personalDataList = userDataDao.findAllByUserId(userId);
//            for(int i=0;i<dataItems.size();i++){
//                personalDataArray.add(dataItems.get(i));
//            }
            o.put("personalData",personalDataList);

            List<ResourceData> publicDataList = resourceDataDao.findAll();
            o.put("publicData",publicDataList);

            List<Theme> themeList=themeDao.findAll();
            o.put("themeList",themeList);

            List<ModelItem> modelItemList= modelItemDao.findAll();
            o.put("modelList",modelItemList);

            return ResultUtils.success(o);

        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"查询失败"+e.getMessage());
        }
    }

    @Override
    public JsonResult deleteFolder(String folderId) {
        try{
            if(deleteFolderCascade(folderId)){
                return ResultUtils.success("删除文件夹成功");
            }else {
                return ResultUtils.error(-1,"删除文件夹失败");
            }
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"删除文件夹失败");
        }
    }

    //递归
    public boolean deleteFolderCascade(String folderId) {
        try {
            folderDao.deleteById(folderId);
            userDataDao.deleteAllByParentId(folderId);
            List<Folder> childFolders = folderDao.findAllByParentId(folderId);
            for (int i = 0; i < childFolders.size(); i++) {
                return deleteFolderCascade(childFolders.get(i).getId());
            }
            return true;
        }catch (Exception e){
            log.error(e.getMessage());
            return false;
        }
    }

    @Override
    public JsonResult deleteDataItem(String dataItemId) {
        try{
            Optional<UserData> dataItemDB= userDataDao.findById(dataItemId);
            UserData userData =dataItemDB.get();

//            如果此数据来自资源中心或他人的public数据，删除实体可能会造成问题
//            File file=new File(dataFolder, userData.getFileStoreName());
//            if(file.exists()){
//                file.delete();
//            }

            String userId = userData.getUserId();
            UserEntity userEntity = userDao.findByUserId(userId);
            ArrayList<String> datalist = userEntity.getDatalist();
            datalist.remove(dataItemId);
            userEntity.setDatalist(datalist);
            userDao.save(userEntity);

            userDataDao.deleteById(dataItemId);

            return ResultUtils.success("删除数据成功");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"删除数据失败");
        }
    }

    @Override
    public void downloadDataItem(String dataItemId, HttpServletResponse response) {
        try{
            UserData userData = userDataDao.findById(dataItemId).get();
            File file=new File(dataFolder, userData.getFileStoreName());
            MyFileUtils.downloadFile(file,response);

        }catch (Exception e){
            log.error(e.getMessage());
        }
    }

    @Override
    public JsonResult getAllPublicUserData() {
        try {
            List<UserData> userDataList=userDataDao.findByPublicBooleanAndInitialIsTrue(true,true);
            return ResultUtils.success(userDataList);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"获取公共数据失败");
        }
    }

    @Override
    public JsonResult saveModel(CreateResourceModelDTO createResourceModelDTO, @RequestPart("imgFile") MultipartFile imgFile, String userId) {
        try{
            File folder = new File(dataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            ModelItem modelItem =new ModelItem();
            BeanUtils.copyProperties(createResourceModelDTO, modelItem);
            modelItem.setMdlJson(ModelUtils.convertMdl(createResourceModelDTO.getMdl()));
            //图像
            if (imgFile.isEmpty()) {
                modelItem.setImgWebAddress("");
            }else {
                //将文件保存到指定位置
                String imgFileName = imgFile.getOriginalFilename(); //eg: XXX.js
                String imgFileMainName= FileNameUtil.mainName(imgFileName); // XXX
                String imgFileExtName = FileNameUtil.extName(imgFileName); //js
                String imgFileNewName=IdUtil.objectId()+"."+imgFileExtName;
                File saveImgFile = new File(folder, imgFileNewName);//eg: E:\\TEMP\\1231231.js
                imgFile.transferTo(saveImgFile);
                modelItem.setImgStoreName(imgFileNewName);
                modelItem.setImgWebAddress("/store/resourceData/"+imgFileNewName);
                modelItem.setImgRelativePath("/resourceData/"+imgFileNewName);
            }
            modelItemDao.save(modelItem);


            UserEntity userEntity = userDao.findByUserId(userId);
            ArrayList<String> tools = userEntity.getToolList();
            tools.add(modelItem.getId());
            userEntity.setToolList(tools);
            userDao.save(userEntity);

            return ResultUtils.success("添加模型条目成功！");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"添加模型条目失败！");
        }
    }

    @Override
    public JsonResult removeModelItem(String modelItemId, String userId) {
        try{
            UserEntity userEntity = userDao.findByUserId(userId);
            ArrayList<String> tools = userEntity.getToolList();;
            tools.remove(modelItemId);
            userEntity.setToolList(tools);
            userDao.save(userEntity);
//            modelItemDao.deleteById(modelItemId);

            return ResultUtils.success("移除模型成功");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"移除模型失败");
        }
    }

    @Override
    public JsonResult getModelItemByUserId(String userId) {
        try {
            UserEntity userEntity = userDao.findByUserId(userId);
            ArrayList<String> tools = userEntity.getToolList();
            ArrayList<ModelItem> models = new ArrayList<>();
            ArrayList<ModelItem> methods = new ArrayList<>();
            for (String tool : tools) {
                Optional<ModelItem> modelItem = modelItemDao.findById(tool);
                if(modelItem.isPresent()){
                    if(modelItem.get().getModelType().equals("model")){
                        models.add(modelItem.get());
                    }else{
                        methods.add(modelItem.get());
                    }
                }
            }
            JSONObject result = new JSONObject();
            result.put("models",models);
            result.put("methods",methods);
            return ResultUtils.success(result);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"获取模型失败");
        }
    }

    @Override
    public JsonResult updateModel(UpdateResourceModelDTO updateResourceModelDTO, MultipartFile imgFile) {
        try{
            File folder = new File(dataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            ModelItem modelItem = modelItemDao.findById(updateResourceModelDTO.getId()).get();
            BeanUtils.copyProperties(updateResourceModelDTO, modelItem);

            modelItem.setMdlJson(ModelUtils.convertMdl(updateResourceModelDTO.getMdl()));

            //图像
            if (imgFile.isEmpty()) {
                modelItem.setImgWebAddress("");
            }else {
                //将文件保存到指定位置
                String imgFileName = imgFile.getOriginalFilename(); //eg: XXX.js
                String imgFileMainName= FileNameUtil.mainName(imgFileName); // XXX
                String imgFileExtName = FileNameUtil.extName(imgFileName); //js
                String imgFileNewName=IdUtil.objectId()+"."+imgFileExtName;
                File saveImgFile = new File(folder, imgFileNewName);//eg: E:\\TEMP\\1231231.js
                imgFile.transferTo(saveImgFile);
                modelItem.setImgStoreName(imgFileNewName);
                modelItem.setImgWebAddress("/store/resourceData/"+imgFileNewName);
                modelItem.setImgRelativePath("/resourceData/"+imgFileNewName);
            }
            modelItemDao.save(modelItem);

            return ResultUtils.success("添加模型条目成功！");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"添加模型条目失败！");
        }
    }

    @Override
    public JsonResult getProjectsByUserId(String userId, PageDTO pageDTO) {
        try {
            Pageable pageable = PageRequest.of(pageDTO.getPage()-1, pageDTO.getPageSize(), Sort.by(pageDTO.getAsc()? Sort.Direction.ASC: Sort.Direction.DESC,pageDTO.getSortField()));
            Page<Project> created = projectDao.findAllByCreatorId(userId, pageable);
            Page<Project> all = projectDao.findAllByMemberListContains(userId, pageable);
            Page<Project> joined = projectDao.findAllByMemberListContainsAndCreatorIdNot(userId, userId, pageable);
            JSONObject result = new JSONObject();
            result.put("created", created);
            result.put("joined", joined);
            result.put("all", all);
            return ResultUtils.success(result);
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"获取项目失败");
        }


    }

    @Override
    public JsonResult updateMethod(UpdateResourceMethodDTO updateResourceMethodDTO, MultipartFile imgFile) {
        try{
            File folder = new File(dataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            ModelItem modelItem = modelItemDao.findById(updateResourceMethodDTO.getId()).get();
            BeanUtils.copyProperties(updateResourceMethodDTO, modelItem);

            //图像
            if (imgFile.isEmpty()) {
                modelItem.setImgWebAddress("");
            }else {
                //将文件保存到指定位置
                String imgFileName = imgFile.getOriginalFilename(); //eg: XXX.js
                String imgFileMainName= FileNameUtil.mainName(imgFileName); // XXX
                String imgFileExtName = FileNameUtil.extName(imgFileName); //js
                String imgFileNewName=IdUtil.objectId()+"."+imgFileExtName;
                File saveImgFile = new File(folder, imgFileNewName);//eg: E:\\TEMP\\1231231.js
                imgFile.transferTo(saveImgFile);
                modelItem.setImgStoreName(imgFileNewName);
                modelItem.setImgWebAddress("/store/resourceData/"+imgFileNewName);
                modelItem.setImgRelativePath("/resourceData/"+imgFileNewName);
            }
            modelItemDao.save(modelItem);

            return ResultUtils.success("添加方法条目成功！");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"添加方法条目失败！");
        }
    }

    @Override
    public JsonResult saveMethod(CreateResourceMethodDTO createResourceMethodDTO, MultipartFile imgFile, String userId) {
        try{
            File folder = new File(dataFolder);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }

            ModelItem modelItem =new ModelItem();
            BeanUtils.copyProperties(createResourceMethodDTO, modelItem);

            //图像
            if (imgFile.isEmpty()) {
                modelItem.setImgWebAddress("");
            }else {
                //将文件保存到指定位置
                String imgFileName = imgFile.getOriginalFilename(); //eg: XXX.js
                String imgFileMainName= FileNameUtil.mainName(imgFileName); // XXX
                String imgFileExtName = FileNameUtil.extName(imgFileName); //js
                String imgFileNewName=IdUtil.objectId()+"."+imgFileExtName;
                File saveImgFile = new File(folder, imgFileNewName);//eg: E:\\TEMP\\1231231.js
                imgFile.transferTo(saveImgFile);
                modelItem.setImgStoreName(imgFileNewName);
                modelItem.setImgWebAddress("/store/resourceData/"+imgFileNewName);
                modelItem.setImgRelativePath("/resourceData/"+imgFileNewName);
            }
            modelItemDao.save(modelItem);


            UserEntity userEntity = userDao.findByUserId(userId);
            ArrayList<String> tools = userEntity.getToolList();
            tools.add(modelItem.getId());
            userEntity.setToolList(tools);
            userDao.save(userEntity);

            return ResultUtils.success("添加方法条目成功！");
        }catch (Exception e){
            log.error(e.getMessage());
            return ResultUtils.error(-1,"添加方法条目失败！");
        }
    }

    @Override
    public JsonResult modelItemToProject(String projectId, List<String> modelItemList, String userId) {
       try {
           List<String> success = new ArrayList<>();
           List<String> failed = new ArrayList<>(modelItemList);
           ComputeModel computeModel = computeModelDao.findByProjectId(projectId);
           List<String> existList = computeModel.getModelList();
           for (String modelItemId : modelItemList) {
               if(!existList.contains(modelItemId)){
                   existList.add(modelItemId);
                   success.add(modelItemId);
                   failed.remove(modelItemId);
               }
           }
           computeModel.setModelList(existList);
           computeModelDao.save(computeModel);

           JSONObject result = new JSONObject();
           result.put("success", success);
           result.put("failed", failed);
           return ResultUtils.success(result);
       } catch (Exception e) {
           return ResultUtils.error(-1, "添加模型/方法条目失败");
       }
    }
}